package de.wbh.gwt.form.layouter.gwt.ui.view;

import java.util.List;

import javax.inject.Inject;

import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandlerAdapter;
import com.allen_sauer.gwt.dnd.client.DragStartEvent;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.drop.CustomDropController;
import com.google.common.base.Objects;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

import de.wbh.gwt.form.layouter.gwt.dnd.Boundary;
import de.wbh.gwt.form.layouter.gwt.ui.view.FieldsViewImpl.Resources.Style;
import de.wbh.gwt.form.layouter.gwt.ui.view.widget.Button;
import de.wbh.gwt.form.layouter.gwt.ui.view.widget.Field;
import de.wbh.gwt.form.layouter.gwt.ui.view.widget.NewFieldPopup;
import de.wbh.gwt.form.layouter.shared.activity.FieldsView;

public class FieldsViewImpl extends Composite implements FieldsView {

	interface FieldsViewImplUiBinder extends UiBinder<Widget, FieldsViewImpl> {
	}

	public static interface Resources extends ClientBundle {
		static interface Style extends CssResource {
			String selected();
		}

		@Source("fieldsViewImpl.css")
		Style style();
	}

	private static FieldsViewImplUiBinder uiBinder = GWT.create(FieldsViewImplUiBinder.class);

	private Presenter presenter;

	@UiField(provided = true)
	VerticalPanel fieldsContainer = new VerticalPanel() {

		@Override
		public boolean remove(Widget w) {
			int index = getWidgetIndex(w);
			String fieldId = ((Field) w).getId();
			if (super.remove(w)) {
				if (FieldsViewImpl.this.dragging) {
					Field field = createField(fieldId);
					FieldsViewImpl.this.fieldsContainer.insert(field, index);
				}
				return true;
			}
			return false;
		}
	};

	private Field selected;

	private final ClickHandler fieldClickHandler = new ClickHandler() {

		@Override
		public void onClick(ClickEvent event) {
			Field field = (Field) event.getSource();
			selectField(field);
		}
	};

	private final Resources resources;

	private final Style style;
	@UiField
	Button removeFieldButton;

	private PopupPanel popupPanel;

	private final PickupDragController dragController;

	private CustomDropController dropController;

	private boolean dragging;

	@Inject
	public FieldsViewImpl(Resources resources, @Boundary AbsolutePanel boundaryPanel) {
		this.resources = resources;
		this.style = resources.style();
		this.style.ensureInjected();
		initWidget(uiBinder.createAndBindUi(this));
		this.dragController = new PickupDragController(boundaryPanel, false) {
			@Override
			protected void restoreSelectedWidgetsLocation() {
				//
			}
		};
		this.dragController.setBehaviorDragStartSensitivity(5);

		this.dragController.addDragHandler(new DragHandlerAdapter() {
			@Override
			public void onDragEnd(DragEndEvent event) {
				super.onDragEnd(event);
				FieldsViewImpl.this.dragging = false;
			}

			@Override
			public void onDragStart(DragStartEvent event) {
				FieldsViewImpl.this.dragging = true;
				for (Widget w : event.getContext().selectedWidgets) {
					((Field) w).setDropped(false);
					w.getElement().getStyle().clearWidth();
					w.getElement().getStyle().clearHeight();
				}
			}
		});
	}

	@Override
	public void addField(String fieldId) {
		Field field = createField(fieldId);
		this.fieldsContainer.add(field);
	}

	@Override
	public void createField() {
		if (this.popupPanel == null) {
			this.popupPanel = new NewFieldPopup(new AsyncCallback<String>() {
				@Override
				public void onFailure(Throwable caught) {
					FieldsViewImpl.this.popupPanel.hide();
				}

				@Override
				public void onSuccess(String result) {
					FieldsViewImpl.this.presenter.fieldCreated(result);
					FieldsViewImpl.this.popupPanel.hide();
				}
			});
		}
		this.popupPanel.center();
	}

	private Field createField(String fieldId) {
		Field field = new Field(fieldId);
		field.addDomHandler(FieldsViewImpl.this.fieldClickHandler, ClickEvent.getType());
		this.dragController.makeDraggable(field);
		return field;
	}

	@Override
	public void onDropControllerAdded(CustomDropController dropController) {
		if (this.dropController != null) {
			this.dragController.unregisterDropController(this.dropController);
		}
		this.dragController.registerDropController(this.dropController = dropController);
		System.err.println("REGISTERED");
	}

	@Override
	public void onFieldsCreated(List<Field> fields) {
		for (Field f : fields) {
			this.dragController.makeDraggable(f);
			f.setDropped(true);
			f.addClickHandler(this.fieldClickHandler);
		}
	}

	@UiHandler("newFieldButton")
	public void onNewFieldButtonClick(ClickEvent event) {
		this.presenter.createField();
	}

	@UiHandler("removeFieldButton")
	public void onRemoveFieldButtonClick(ClickEvent event) {
		if (this.selected != null) {
			if (Objects.equal(this.selected.getParent(), this.fieldsContainer)) {
				this.presenter.removeFromEfd(this.selected.getId());
			} else {
				this.presenter.onFieldRemoved();
			}
			this.selected.removeFromParent();
		}
	}

	private void selectField(Field field) {
		if (this.selected != null) {
			this.selected.removeStyleName(this.style.selected());
		}

		if (Objects.equal(this.selected, field)) {
			this.selected = null;
		} else {
			this.selected = field;
			if (this.selected != null) {
				this.selected.addStyleName(this.style.selected());
			}
		}
		this.removeFieldButton.setEnabled(this.selected != null);
	}

	@Override
	public void setPresenter(Presenter presenter) {
		this.presenter = presenter;
		presenter.onDragControllerAdded(this.dragController);
	}

	@Override
	public void showFields(Iterable<String> fields) {
		for (String fieldId : fields) {
			addField(fieldId);
		}
	}
}
